const PENDING = 'pending'
const FULFILLED= 'fulfilled'
const REJECTED = 'rejected'

class MPromise {
  FULFILLED_CALLBACK_LIST = []
  REJECTED_CALLBACK_LIST = []
  _status = PENDING
  /**
   * 
   * @param {Function} fn  function
   */
  constructor (fn) {
    this.status = PENDING
    this.value = null
    this.reason = null
    try {
      fn(this.resolve.bind(this), this.reject.bind(this))
    } catch (e) {
      this.reject(e)
    }
  }
  then (onFulfilled, onRejected) {
    const realOnFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
    const realOnRejected = this.isFunction(onRejected) ? onRejected : reason => reason

    let promise2 = new MPromise((resolve, reject) => {
      const fulfilledMicrotask = () => {
        queueMicrotask(()=>{
          try {
            const x = realOnFulfilled(this.value)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      const rejectedMicrotask = () => {
        queueMicrotask(()=>{
          try {
            console
            const x = realOnRejected(this.reason)
            console.log('x=== ',x)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      switch (this.status) {
        case FULFILLED:
          fulfilledMicrotask()
          break;
        case REJECTED:
          rejectedMicrotask()
          break;
        case PENDING: {
          this.FULFILLED_CALLBACK_LIST.push(fulfilledMicrotask)
          this.REJECTED_CALLBACK_LIST.push(rejectedMicrotask)
          break
        }
        default:
          break;
      }

    })
    return promise2
  }
  catch (onRejected) {
    console.log(onRejected)
    return this.then(null, onRejected)
  }
  resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      return reject(new TypeError('the promise and the return value is same'))
    }
    if (x instanceof MPromise) {
      queueMicrotask(()=>{
        x.then(y=>{
          this.resolvePromise(promise2, y, resolve, reject)
        }, reject)
      })
    } else if (typeof x === 'object' || this.isFunction(x)) {
      if (x === null) return resolve(x)
      let then = null
      try {
        then = x.then
      } catch (error) {
        reject(error)
      }
      if (this.isFunction(then)) {
        let called = false
        try {
          then.call(
            x,
            y=>{
              if (called) return
              called = true
              this.resolvePromise(promise2,y,resolve,reject)
            },
            r=>{
              if (called) return
              called = true
              this.resolvePromise(promise2,r,resolve,reject)
            }
          )
        } catch (error) {
          if (called) return
          reject(error)
        }
      } else {
        resolve(x)
      }
    } else {
      resolve(x)
    }
  }

  get status () {
    return this._status
  }
  set status (newStatus) {
    this._status = newStatus
    switch (newStatus) {
      case FULFILLED:
        this.FULFILLED_CALLBACK_LIST.forEach(cb => cb(this.value))
        break;
      case REJECTED:
        this.REJECTED_CALLBACK_LIST.forEach(cb => cb(this.reason))
        break;
    }
  }
  static resolve (value) {
    if (value instanceof MPromise) {
      return value
    }
    return new MPromise((resolve,reject)=>{
      resolve(value)
    })
  }

  static reject (reason) {
    return new MPromise((resolve, reject) => {
      reject(reason)
    })
  }

  static race (promiseList) {
    return new MPromise((resolve,reject)=>{
      if (promiseList.length == 0) {
        return resolve()
      } else {
        for (let index = 0; index < promiseList.length; index++) {
          MPromise.resolve(promiseList[index]).then(
            value => resolve(value),
            reason => reject(reason)
          )
        }
      }
    })
  }

  isFunction (val) {
    return typeof val === 'function'
  }

  resolve (value) {
    if (this.status === PENDING) {
      this.value = value
      this.status = FULFILLED
    }
  }

  reject (reason) {
    if (this.status === PENDING) {
      this.reason = reason
      this.status = REJECTED
    }
  }

}

// let promise = new MPromise((resolve, reject) => { 
//   setTimeout(()=>{
//     reject('11111')
//   },1000)
// }).catch(reason=>{
//   console.log('this.is reason',reason)
//   console.log(promise)
// })
// setTimeout(()=>{
//   console.log(promise)
// },3000)

// let p1 =  MPromise.resolve('2222222222')
// console.log(p1)

let p1 = new MPromise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('1--1')
  },1000)
})

let p2 = new MPromise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('2--2')
  },2000)
})

let p3 = new MPromise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('3--3')
  },500)
})

MPromise.race([p1,p2,p3]).then(console.log)